#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <ctype.h>
#include <mpi.h>

void geraPalavra(char palavra[], char caracteresIN[], int pCaraIN) {
	char palavraRand[100]/*palavra que será gerada randomicamente*/, auxPalavra[6]/*Auxiliar para pegar parte de palavras maiores que 5*/;
	int tamPalavra, i, j = 0, k;
	
	srand(time(NULL));
	
	tamPalavra = strlen(palavra); //pega o tamanho da palavra
	
	if(tamPalavra > 5) {
		for(i = 0; i <= tamPalavra; i++) { //percorrendo a palavra dividindo-a em pedaços de 5
			if(j == 5 || i == tamPalavra) { //chegou ao fim da palavra
				auxPalavra[j] = '\0';
				j = 0;
				
				//printf("Gerando o pedaco %s da palavra %s\n", auxPalavra, palavra);
				
				do { //gera palavras menores que 5
					for(k = 0; k < strlen(auxPalavra); k++) { 
						palavraRand[k] = caracteresIN[rand() % pCaraIN]; //gera a palavra randomicamente com os caracteres que estão no vetor de caracteres que aquela palavra possui
					}
					palavraRand[k] = '\0';
				} while(strcmp(auxPalavra, palavraRand) != 0);
				
				if(i != tamPalavra) { //esse if server para não pular letra ao cortar a palavra
					auxPalavra[j] = palavra[i];
					j++;
				}
			}
			else { //pegando caracter por caracter
				auxPalavra[j] = palavra[i];
				j++;	
			}
		}
	}
	else {
		do { //gera palavras menores que 5
			for(i = 0; i < tamPalavra; i++) { 
				palavraRand[i] = caracteresIN[rand() % pCaraIN]; //gera a palavra randomicamente com os caracteres que estão no vetor de caracteres que aquela palavra possui
			}
			palavraRand[i] = '\0';
		} while(strcmp(palavra, palavraRand) != 0);
	}
}

int main(int argc, char *argv[]) {
	FILE *pArq;
   	long nPalavras, nPalavrasProc, auxNPalavras;
   	int i = 0;
   	
   	pArq = fopen ( "filtrado.txt" , "r" );
   	
	if (pArq == NULL) {
		fputs ("Utilize o filtro\n",stderr); 
		exit (1);
	}
    
    fscanf(pArq, "%ld", &nPalavras); //pega o número total de palavras escrito no arquivo
    
    auxNPalavras = nPalavras; //armazena em um auxiliar

	//MPI
	int ret, nProcessos, rank; 
    MPI_Status stat;
	
    ret = MPI_Init(&argc, &argv);
    if(ret != MPI_SUCCESS)
		exit(1);
		
    MPI_Comm_size(MPI_COMM_WORLD, &nProcessos);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    nPalavrasProc = ( nPalavras / (nProcessos - 1) ) + 1; //número de palavras que cada processo recebera
    
    char listaPalavrasIN[nPalavrasProc][100], listaPalavrasOUT[nPalavrasProc][100]; //listas de palavras que os processos receberao
    char caracteresIN[50], caracteresOUT[50]; //lista de caracteres que possuem os caracteres suficientes para gerar as palavras
    double inicioFilho = 0, fimFilho = 0, inicio = 0, fim = 0; //variaveis de tempo
        
    if(rank == 0) {
    	long npg = 0, npgIN = 0; //numero de palavras geradas
    	int j;
    	float porcentagem; //porcentagem de palavras geradas
    	
		inicio = MPI_Wtime(); //inicia a execução.
    	
    	for(i = 97; i < 123; i++) { //preenchendo o vetor de caracteres com as letras do alfabeto, minusculas
    		caracteresOUT[i-97] = i;
    	}
    	caracteresOUT[i-97] = -1;
    	
    	for(i = 1; i < nProcessos; i++) {
    		if(nPalavrasProc < auxNPalavras) {
    			for(j = 0; j < nPalavrasProc; j++) { //lendo as palavras do arquivo e armazenando na lista
    				fscanf(pArq, "%s", &listaPalavrasOUT[j]);
    			}
    			auxNPalavras = auxNPalavras - nPalavrasProc; 
    		}
    		else { //ultimo processo recebe um numero menor de palavras
    			for(j = 0; j < auxNPalavras; j++) {
    				fscanf(pArq, "%s", &listaPalavrasOUT[j]); //lendo as palavras do arquivo e armazenando na lista
    			}
    			listaPalavrasOUT[j][0] = -1; //como nao preencheu a lista inteira por ser ultimo processo seta o final com -1 
    		}

			MPI_Send(&listaPalavrasOUT, (nPalavrasProc)*(100), MPI_CHAR, i, 1, MPI_COMM_WORLD); //envia lista de palavras
			MPI_Send(&caracteresOUT, 50, MPI_CHAR, i, 1, MPI_COMM_WORLD); //envia lista de caracteres
			MPI_Send(&inicio, 1, MPI_DOUBLE, i, 1, MPI_COMM_WORLD); //envia tempo inicial
		}
		
		for(i = 1; i < nProcessos; i++) {
			MPI_Recv(&npgIN, 1, MPI_LONG, i, 1, MPI_COMM_WORLD, &stat); //recebe o numero de palavras geradas de cada filho
			MPI_Recv(&fim, 1, MPI_DOUBLE, i, 1, MPI_COMM_WORLD, &stat); //recebe o tempo final calculado
			npg += npgIN; //soma o numero de palavras geradas de cada filho
		}
		
		printf("%ld palavras geradas\n", npg);
		
	    //tempo de execução
    	double  tempo = (fim - inicio)/60;
    	
    	porcentagem = ((float)npg/(float)nPalavras)*100;
    	
    	printf("[ %.2f %] do total de palavras (%ld) foram geradas em %.2lf minutos\n", porcentagem, nPalavras, tempo);
    }
    else {
    	int j, pCaraIN = 26/*"ponteiro" para o vetor caracteresIN, na posição 26 pois tem 26 letras no vetor*/;
    	long npgOUT = 0; /*contador para o numero de palavras geradas*/

    	MPI_Recv(&listaPalavrasIN, (nPalavrasProc)*(100), MPI_CHAR, 0, 1, MPI_COMM_WORLD, &stat);//recebe o pedaço do arquivo para gerar as palavras
    	MPI_Recv(&caracteresIN, 50, MPI_CHAR, 0, 1, MPI_COMM_WORLD, &stat); //recebe o vetor dos caracteres criado no pai
    	MPI_Recv(&inicioFilho, 1, MPI_DOUBLE, 0, 1, MPI_COMM_WORLD, &stat); //recebe tempo inicial
    	
    	for(i = 0; i < nPalavrasProc; i++) { //laço para percorrer o pedaço do arquivo recebido do pai
    		if(listaPalavrasIN[i][0] == -1) { //chegou no final da lista recebida pelo pai provavelmente no ultimo filho
    			i = nPalavrasProc;
    		} 
    		else {
				for(j = 0; j < strlen(listaPalavrasIN[i]); j++) {
					if(!isalpha(listaPalavrasIN[i][j])) { //se o char não for alfabético armazena ele no vetores de caracteres para gerar a palavra
						caracteresIN[pCaraIN] = listaPalavrasIN[i][j];
						pCaraIN++;
						caracteresIN[pCaraIN] = -1; //seta o final do vetor de caracteres com -1
					}
				}
	   					
	   			printf("Filho %d tentando gerar a palavra %s...\n", rank, listaPalavrasIN[i]);
				geraPalavra(listaPalavrasIN[i], caracteresIN, pCaraIN);//gerar palavra
				printf("Filho %d gerou a palavra %s\n", rank, listaPalavrasIN[i]);
	   					
				npgOUT++; //incrementa o numero de palavras geradas    	
				
				fimFilho = MPI_Wtime();
				if((fimFilho - inicioFilho)/60 > 5/*Tempo em minutos da execucao do programa*/) { //condicao de parada
					i = nPalavrasProc;
				}			
	   					
	   			pCaraIN = 26; //reinicia o "ponteiro" para o vetor de caracteres
	   			caracteresIN[pCaraIN] = -1; //reinicia o vetor de caracteres
    		}
    	}
    	
    	MPI_Send(&npgOUT, 1, MPI_LONG, 0, 1, MPI_COMM_WORLD); //envia o numero de palavras geradas
    	MPI_Send(&fimFilho, 1, MPI_DOUBLE, 0, 1, MPI_COMM_WORLD); //envia o tempo final
    }	

	MPI_Finalize();
	fclose (pArq);
	
    return 0;	  
}
